
#ifndef __CORTISOL_WGL_H
#define __CORTISOL_WGL_H

#include <windows.h>

#define GL_GLEXT_PROTOTYPES 1
#include <GL/GL.h>
#include <wgl/glext.h>
#include <wgl/wglext.h>

#include <exception>
#include <string>
#include <vector>
#include <map>

class WGLWindow
{
	const LPCWSTR	name;
	int				adapter;
	bool			fullscreen;
	WNDPROC			windowProc;
	HMONITOR		hMonitor;
	RECT			monitorRect;
	HWND			hWindow;
	HDC				hDC;
	HGLRC			hRC;
	HANDLE			hThread;
	int				width;
	int				height;
	bool			isCurrent;
	bool			closeFlag;

	/*** WGL EXTENSIONS ***/
	PFNWGLCHOOSEPIXELFORMATARBPROC		wglChoosePixelFormatARB;
	PFNWGLCREATECONTEXTATTRIBSARBPROC	wglCreateContextAttribsARB;

	/*** USER data struct for monitor enumeration ***/
	struct MonitorEnumStruct {

		MonitorEnumStruct(WGLWindow *caller)
			:	caller(caller),
			iteration(0) 
		{}

		WGLWindow   *caller;
		int			iteration;
	};

	/*** Record window instances on a per thread basis, 
		and keep track of current rendering context. ***/
	struct WindowListStruct {

		typedef std::vector<WGLWindow*>			WindowVector;
		typedef std::map<HANDLE, WindowVector>	WindowVectorMap;

		WindowListStruct()
			:	mutex( NULL ),
				windowVectorMap()
		{}

		HANDLE			mutex;
		WindowVectorMap windowVectorMap;
	};

	static WindowListStruct windowList;

public:

	/*** WGL error exception class ***/
	class WglException : public std::exception
	{
		std::string _what;
		bool threadMismatch;
	public:

		WglException(const char *_what, bool threadMismatch = false)
			:	_what(_what),
				threadMismatch(threadMismatch)
		{}

		WglException(const std::string &_what, bool threadMismatch = false)
			:	_what(_what),
				threadMismatch(threadMismatch)
		{}

		virtual ~WglException(){}

		const char* what() const throw()
		{
			return _what.c_str();
		}

		bool IsThreadMismatch() const throw() { return threadMismatch; }
	};

	/*** Initialise static members ***/
	static void StaticInitialise();

	/*** Free static resources ***/
	static void StaticDispose();

	/*** CTOR ***/
	WGLWindow(const LPCWSTR name,int adapter, int width, int height, bool fullscreen, WNDPROC windowProc = NULL);

	/*** DTOR ***/
	virtual ~WGLWindow() throw();

	/*** Make this window the current rendering context for this thread ***/
	void MakeCurrent( bool current );

	/*** Show back buffer ***/
	void Present();

	/*** Is the window the target of the current rendering context? ***/
	bool IsCurrent() const { return isCurrent; }

	/*** Determine the number of monitors attached ***/
	static int	GetMonitorCount();

	/*** Default WNDPROC - forward window messages to correct WGLWindow instance ***/
	static LRESULT CALLBACK GlobalWindowMessageProcessor(HWND window, UINT message, WPARAM wParam, LPARAM lParam);

	void Update();

	bool ShouldClose() const;

protected:

	/*** Over-ridable Window message handler - Only used when you use the default windowProc in the constructor ***/
	virtual LRESULT WindowMessageProcessor(HWND window, UINT message, WPARAM wParam, LPARAM lParam);

	// Called when a window from the same thread group calls MakeCurent(true);
	virtual void InformNolongerCurrent();

	// Window class style
	virtual DWORD GetStyle() const;

	// Window class extended style
	virtual DWORD GetExStyle() const;

	virtual const int* GetPixelAttributes() const;

private:

	// dummy window ctor - we need a valid gl context for making use of wgl extensions.
	WGLWindow(const LPCWSTR name,int adapter,WNDPROC windowProc);

	// Initialise wgl extensions
	void InitialiseWgl();

	// Create window
	void InitialiseWindow();

	// Setup window pixel format
	void InitialisePixelFormat();

	// Pixel format for Dummy Window
	void InitialiseDummyPixelFormat();

	// Create OpenGL Context
	void CreateContext( HGLRC hShareContext = NULL );

	// Initialise All
	void Initialise();

	// Set window size / change monitor resolution.
	void SetResolution();

	// Read monitor data for this adapter.
	void GetMonitor();

	// MonitorEnum callback for this instance.
	BOOL MonitorEnum(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,MonitorEnumStruct *data);

	// Global MonitorEnum callback.
	static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData);

	ATOM RegisterWindowClass(const LPCWSTR name) const;

	static std::string GetLastWindowsError();

	// Cleanup
	void Dispose() throw();

};

#endif /*** __CORTISOL_WGL_H ***/

